/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.sgf.core.interceptor;

import com.inspur.edp.sgf.api.utils.ObjectMapperUtil;
import com.inspur.edp.sgf.core.cache.ConfigCacheResult;
import com.inspur.edp.sgf.core.cache.GlobalConfigCacheResult;
import com.inspur.edp.sgf.core.common.EapiOpConfig;
import com.inspur.edp.sgf.core.exception.EapiAuthorizationException;
import com.inspur.edp.sgf.core.manager.EapiConfigManager;
import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.permission.api.manager.runtime.PermissionManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.jaxrs.model.OperationResourceInfo;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Path;
import java.util.Map;

/**
 * 功能描述:
 *
 * @ClassName: EapiConfigCxfInterceptor
 * @Author: Fynn Qi
 * @Date: 2020/12/17 18:16
 * @Version: V1.0
 */
public class EapiConfigCxfInterceptor extends AbstractPhaseInterceptor {

    private final Logger logger = LoggerFactory.getLogger(EapiConfigCxfInterceptor.class);

    private final String GSP_RPC = "gsp-rpc";

    private final String GSP_CONTEXT = "gsp-context";

    private final String STRING_TRUE = "true";

    private EapiConfigManager configManager;

    private PermissionManager permissionManager;

    public EapiConfigCxfInterceptor(EapiConfigManager configManager, PermissionManager permissionManager) {
        super(Phase.PRE_INVOKE);
        this.configManager = configManager;
        this.permissionManager = permissionManager;
    }

    @Override
    public void handleMessage(Message message) throws Fault {

        if (isRequestor(message)) {
            return;
        }

        //获取HttpServletRequest
        HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);

        if (request == null) {
            throw new RuntimeException("未获取到HttpServletRequest");
        }

        if (StringUtils.isNotBlank(request.getHeader(GSP_CONTEXT))) {
            Map map = ObjectMapperUtil.deserialize(request.getHeader(GSP_CONTEXT), Map.class);
            if (map != null && map.containsKey(GSP_RPC) && STRING_TRUE.equals(map.get(GSP_RPC))) {
                return;
            }
        }

        if (CAFContext.current == null || CAFContext.current.getCurrentSession() == null || StringUtils.isBlank(CAFContext.current.getCurrentSession().getUserId())) {
            return;
        }

        //获取EApi配置
        GlobalConfigCacheResult globalConfigCacheResult = configManager.getEapiGlobalConfig();

        //不启用则直接放行
        if (globalConfigCacheResult == null || globalConfigCacheResult.getEnable() == null || !globalConfigCacheResult.getEnable()) {
            return;
        }

        String requestUri = request.getRequestURI();

        if (StringUtils.isBlank(requestUri) || !requestUri.startsWith("/api")) {
            return;
        }

        Exchange exchange = message.getExchange();

        OperationResourceInfo operationResourceInfo = exchange.get(OperationResourceInfo.class);
        if (operationResourceInfo == null) {
            return;
        }

        String uri = getUri(exchange, operationResourceInfo);

        //获取HTTP方法
        String httpMethod = request.getMethod();

        //获取EApi配置
        ConfigCacheResult cacheResult = configManager.getEapiConfigs(uri, httpMethod);

        //如果不存在API配置信息
        if (cacheResult == null || cacheResult.getConfig() == null) {
            return;
        }

        EapiOpConfig config = cacheResult.getConfig();

        //1. API操作不启用权限控制，直接放行
        //2. 不存在功能操作配置，直接放行，我们认为该API不受操作权限控制
        if (config.getEnable() == null || !config.getEnable() || config.getBizOpIdList() == null || config.getBizOpIdList().size() <= 0) {
            return;
        }

        //权限验证
        Map<String, Boolean> result = permissionManager.isPrincipalHasOps(config.getBizOpIdList());

        //采用逻辑运算符 |（或）的验证关系
        if (result != null && result.values().size() > 0 && !result.values().contains(true)) {
            throw new EapiAuthorizationException(String.format("API操作[%s]%s未授权", httpMethod.toUpperCase(), uri));
        }
    }

    private String getUri(Exchange exchange, OperationResourceInfo operationResourceInfo) {

        //获取Endpoint的path
        String baseRequestUri = exchange.getEndpoint().getEndpointInfo().getAddress();

        //获取类上的URITemplate
        String classUriTemplate = "";

        if (operationResourceInfo.getClassResourceInfo() != null && operationResourceInfo.getClassResourceInfo().getURITemplate() != null) {
            classUriTemplate = operationResourceInfo.getClassResourceInfo().getURITemplate().getValue();
        }

        //获取方法上的URITemplate
        String methodUriTemplate = "";

        if (operationResourceInfo.getAnnotatedMethod() != null) {
            Path pathAnnotation = operationResourceInfo.getAnnotatedMethod().getAnnotation(Path.class);
            if (pathAnnotation != null) {
                methodUriTemplate = pathAnnotation.value();
            }
        }

        //build完整的uri
        return buildUri(baseRequestUri, classUriTemplate, methodUriTemplate);
    }

    private String buildUri(String baseRequestUri, String classUriTemplate, String methodUriTemplate) {

        String uri = "/api";

        //拼接baseURI
        uri = appendUri(baseRequestUri, uri);

        //拼接类上的URITemplate
        uri = appendUri(classUriTemplate, uri);

        //拼接方法上的URITemplate
        uri = appendUri(methodUriTemplate, uri);

        return uri;
    }

    private String appendUri(String subUriTemplate, String uri) {
        if (StringUtils.isBlank(subUriTemplate)) {
            return uri;
        }
        String[] segmentArray = subUriTemplate.split("/");
        if (segmentArray.length <= 0) {
            return uri;
        }
        for (String segment : segmentArray) {
            if (StringUtils.isNotBlank(segment)) {
                uri = String.format("%s/%s", uri, segment);
            }
        }
        return uri;
    }

}
